#pragma once
#include <iostream>
#include <wrl/client.h>
#include <d3d9.h>
#include <_IMG_MATCH_.h>
#include <wchar.h>
#include "../InputEntry/InputEntry.h"
#include "../publicextern.h"
#include "../GameLog.h"
#include "../resource.h"

using Microsoft::WRL::ComPtr;

class ImageMatch
{
public:
	ImageMatch() = default;
	~ImageMatch() = default;
public:
	static ImageMatch* getInstance()
	{
		_M_mutex.lock();
		if (!_M_instance)
		{
			_M_instance = new ImageMatch();
			if (_M_instance->init() != S_OK)
			{
				delete _M_instance;
				_M_instance = nullptr;
			}
		}
		_M_mutex.unlock();
		return _M_instance;
	}

	static void release()
	{
		_M_mutex.lock();
		if (_M_instance)
		{
			delete _M_instance;
			_M_instance = nullptr;
		}
		_M_mutex.unlock();
	}

	HRESULT init()
	{
		RECT rect = InputEntry::_M_screen_rect->get();
		_M_src_img.width = rect.right - rect.left;
		_M_src_img.height = rect.bottom - rect.top;

		IDirect3D9Ex* d3d;
		HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d);
		D3DPRESENT_PARAMETERS d3dpp = { 0 };
		d3dpp.BackBufferWidth = _M_src_img.width;
		d3dpp.BackBufferHeight = _M_src_img.height;
		d3dpp.BackBufferCount = 0;
		d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
		d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		d3dpp.EnableAutoDepthStencil = FALSE;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		d3dpp.Windowed = TRUE;
		d3dpp.hDeviceWindow = NULL;
		hr = d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, nullptr
			, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE | D3DCREATE_FPU_PRESERVE
			, &d3dpp, nullptr, &_M_d3d_device);
		d3d->Release();

		hr = _M_d3d_device->CreateOffscreenPlainSurface(_M_src_img.width
			, _M_src_img.height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM
			, &_M_surface, nullptr);
		return hr;
	}

	bool findTargetImg(_IMG_RAW_& target, POINT& pt, double threshold = 0.01)
	{
		DOUBLE rst;
		D3DLOCKED_RECT rect;
		time_t tm_start = clock();
		HRESULT hr = _M_d3d_device->GetFrontBufferData(0, _M_surface.Get());
		_M_surface->LockRect(&rect, nullptr, 0);
		_M_src_img.pits = (BYTE*)rect.pBits;
		img_find(_M_src_img, target, rst, pt);
		_M_surface->UnlockRect();
		printf("[ATL]ImageMatch result:%f pos(%d , %d) spend time:%d\n", rst, pt.x, pt.y, clock() - tm_start);
		WriteToLog(g_pStreamCoreLog, __FILE__, __LINE__, LOG_LEVEL_INFO, "ImageMatch result : %f pos(%d, %d) spend time : %d\n", rst, pt.x, pt.y, clock() - tm_start);
		return rst < threshold;
	}

	bool findFromImages(std::vector<_IMG_RAW_>& vec, POINT& pt, double threshold = 0.01)
	{
		for (auto& r : vec)
		{
			if (findTargetImg(r, pt, threshold))
			{
				return true;
			}
		}
		return false;
	}

	static int loadTargetImage(int IMG_id, _IMG_RAW_* out)
	{
		HINSTANCE hins = (HINSTANCE)GetModuleHandle(TEXT("DL_2IMG69ZKLH.dll"));
		HBITMAP ptrbmap = LoadBitmap(hins, MAKEINTRESOURCE(IMG_id));
		if (!ptrbmap)
		{
			return -1;
		}
		BITMAP bitmap;
		GetObject(ptrbmap, sizeof(BITMAP), &bitmap);
		out->width = bitmap.bmWidth;
		out->height = bitmap.bmHeight;
		out->pits = new BYTE[bitmap.bmWidthBytes * bitmap.bmHeight];
		GetBitmapBits(ptrbmap, bitmap.bmWidthBytes * bitmap.bmHeight, out->pits);
		DeleteObject(ptrbmap);
		return 0;
	}

	/*static int loadTargetImage(const wchar_t* IMG_Path, _IMG_RAW_* out)
	{
		HINSTANCE hins = (HINSTANCE)GetModuleHandle(TEXT("DL_2IMG69ZKLH.exe"));
		HBITMAP ptrbmap = (HBITMAP)LoadImage(NULL, IMG_Path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if (!ptrbmap)
		{
			return -1;
		}
		BITMAP bitmap;
		GetObject(ptrbmap, sizeof(BITMAP), &bitmap);
		out->width = bitmap.bmWidth;
		out->height = bitmap.bmHeight;
		out->pits = new BYTE[bitmap.bmWidthBytes * bitmap.bmHeight];
		GetBitmapBits(ptrbmap, bitmap.bmWidthBytes * bitmap.bmHeight, out->pits);
		DeleteObject(ptrbmap);
		return 0;
	}*/

private:
	ComPtr<IDirect3DDevice9Ex> _M_d3d_device;
	ComPtr<IDirect3DSurface9>  _M_surface;
	_IMG_RAW_				   _M_src_img;
	static ImageMatch*		   _M_instance;
	static std::mutex		   _M_mutex;
};

#define g_mch ImageMatch::getInstance()

class MGuard
{
public:
	MGuard()
	{
		ImageMatch::getInstance();
	}

	bool isValid()
	{
		return ImageMatch::getInstance();
	}

	~MGuard()
	{
		ImageMatch::release();
	}
};